Skip to content

feat: add script to reencrypt wallet with v2 encryption#9204

Open
bdesoky wants to merge 1 commit into
masterfrom
WCN-174-reencrypt
Open

feat: add script to reencrypt wallet with v2 encryption#9204
bdesoky wants to merge 1 commit into
masterfrom
WCN-174-reencrypt

Conversation

@bdesoky

@bdesoky bdesoky commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Ticket: WCN-174

This pull request introduces significant enhancements to the wallet encryption upgrade process and improves compatibility for PDF keycard generation in Node.js environments. The main changes include adding a new script to automate wallet keychain re-encryption and updating the keycard drawing utilities to support server-side (Node.js) usage.

Key changes:

1. New Wallet Encryption Upgrade Script

  • Added scripts/upgrade-wallet-encryption.ts, a comprehensive CLI tool to upgrade wallet keychains from v1 (SJCL/PBKDF2-SHA256 + AES-256-CCM) to v2 (Argon2id + AES-256-GCM) encryption, with support for regenerating the keycard PDF and handling various edge cases (e.g., changed passphrases, legacy backups).

2. Node.js Compatibility for Keycard PDF Generation

  • Updated drawKeycard and related functions in modules/key-card/src/drawKeycard.ts to detect Node.js environments and handle image and QR code rendering appropriately (e.g., using base64 data URLs instead of DOM elements). [1] [2] [3]
  • Modified QR code image handling to allow both HTMLCanvasElement and base64 string inputs, ensuring PDF generation works in both browser and Node.js contexts. [1] [2]

These changes ensure that wallet upgrades and keycard regeneration can be performed reliably in automated or server-side workflows, improving maintainability and operational flexibility.

@linear-code

linear-code Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

WCN-174

@bdesoky bdesoky force-pushed the WCN-174-reencrypt branch 3 times, most recently from 00ed82a to 567bcdf Compare July 8, 2026 20:12
@bdesoky bdesoky changed the title feat: add sdk method to reencrypt wallet with v2 encryption feat: add script to reencrypt wallet with v2 encryption Jul 8, 2026
@bdesoky bdesoky force-pushed the WCN-174-reencrypt branch from 567bcdf to a30b584 Compare July 8, 2026 20:18
vmccarty
vmccarty previously approved these changes Jul 9, 2026

@vmccarty vmccarty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The instructions read well to me 👍 I just have one question about the "dry-run", but it doesn't seem too high priority.

Comment thread scripts/upgrade-wallet-encryption.ts Outdated
console.log(`Wallet: ${wallet.label()} (${walletId})`);

// Fetch passcodeEncryptionCode — required to decrypt Box D and to generate Box D in the new keycard.
// Skipped in dry-run if not provided, since no keycard is produced anyway.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a dry run? Just like a test? Will this be something the client developers already have some frame of reference for, or does this need some kind of explanation somewhere?

@bdesoky bdesoky Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually updated this so that it isnt skipped in dry-run when boxD is provided but forgot to push the change 😅
A dry run will attempt the decrypt + reencrypt but wont update anything in the backend. Just a way to test that the passphrase/boxD work properly

@bdesoky bdesoky force-pushed the WCN-174-reencrypt branch from a30b584 to db796c3 Compare July 9, 2026 17:45
@bdesoky bdesoky marked this pull request as ready for review July 9, 2026 18:01
@bdesoky bdesoky requested review from a team as code owners July 9, 2026 18:01
@bdesoky bdesoky requested review from Marzooqa and s84krish July 9, 2026 18:01
@bdesoky bdesoky force-pushed the WCN-174-reencrypt branch from db796c3 to 4d55f08 Compare July 9, 2026 18:54
@bdesoky bdesoky force-pushed the WCN-174-reencrypt branch from 4d55f08 to ab75384 Compare July 9, 2026 21:59

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this belongs in a different PR

// SJCL envelopes have no "v" field or v=1
if (!envelope.v || envelope.v === 1) return 1;
} catch {
// not JSON

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we want to about this condition?

'passcoderecovery endpoint did not return a passcodeEncryptionCode — pass --passcodeEncryptionCode manually'
);
}
return recoveryInfo.passcodeEncryptionCode as string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't cast, validate

* jsPDF reads `.src` in Node.js; computeKeyCardImageDimensions reads `.width`/`.height`.
* This replicates what the BitGo UI does before calling drawKeycard.
*/
async function loadKeycardImage(url: string): Promise<HTMLImageElement | undefined> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you silently suppressing all errors?

Comment on lines +141 to +157
const args = process.argv.slice(2);
const get = (flag: string): string | undefined => {
const idx = args.indexOf(flag);
return idx !== -1 ? args[idx + 1] : undefined;
};

const env = (get('--env') ?? 'test') as EnvironmentName;
const coin = get('--coin');
const walletId = get('--walletId');
const passphrase = get('--passphrase');
const accessToken = get('--accessToken');
const otp = get('--otp');
const boxD = get('--boxD');
const boxA = get('--boxA');
const boxB = get('--boxB');
const passcodeEncryptionCode = get('--passcodeEncryptionCode');
const dryRun = args.includes('--dry-run');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a real option parser, I believe we typically use yargs

// Main
// ---------------------------------------------------------------------------

async function main() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is long and complicated enough that it deserves to be in modules/ properly, with a test

maybe even in modules/key-card/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants